/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-08-23 16:25:32
 * @LastEditTime: 2021-08-26 18:05:11
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "sdkconfig.h"
#ifndef SDK_CONFIG_H__
    #warning "Please include sdkconfig.h"
#endif
#include "ft_assert.h"
#include "fsleep.h"
#include "gmac_hw.h"
#include "phy_common.h"
#define   PHY_INVALID_ID      0x1fffffff

#ifdef CONFIG_F_GMAC_PHY_AR803X
#include "ar803x_phy.h"

#define   GMAC_PHY_ID1  GMAC_AR803X_PHY_ID1
#define   GMAC_PHY_ID2  GMAC_AR803X_PHY_ID2
#else

#define   GMAC_PHY_ID1  (0xffff)
#define   GMAC_PHY_ID2  (0xffff)

#endif

/**
 * @name: GmacPhyReadReg
 * @msg: read PHY register from MII interface
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u16} phyReg
 * @param {u32} *pRegVal
 */
u32 GmacPhyReadReg(GmacCtrl *pCtrl, u16 phyReg, u32 *pRegVal)
{
    FT_ASSERTZERONUM(pCtrl && pRegVal);
    u32 cmdRegVal = 0, readRegVal = 0;
    GmacConfig *pConfig = &pCtrl->config;
    int timeout = GMAC_RETRY_TIMES;

    cmdRegVal |= GMAC_MII_ADDR_CR(pConfig->clkMDC);
    cmdRegVal |= GMAC_MII_ADDR_PA(pConfig->phyAddr);
    cmdRegVal |= GMAC_MII_ADDR_GR(phyReg);
    cmdRegVal &= ~GMAC_MII_ADDR_GW;
    cmdRegVal |= GMAC_MII_ADDR_GB;

    do
    {
        fsleep_millisec(1);
        readRegVal = GMAC_READ_REG(pCtrl, GMAC_GMII_ADDR_OFFSET);
    } while ((GMAC_MII_ADDR_GB & readRegVal) && (0 <= --timeout));    

    if (0 >= timeout)
    {
        GMAC_ERROR("timeout when do mdio read");
        return GMAC_ERR_TIMEOUT;
    }  

    GMAC_WRITE_REG(pCtrl, GMAC_GMII_ADDR_OFFSET, cmdRegVal);

    do
    {
        fsleep_millisec(1);
        readRegVal = GMAC_READ_REG(pCtrl, GMAC_GMII_ADDR_OFFSET);
    } while ((GMAC_MII_ADDR_GB & readRegVal) && (0 <= --timeout));    

    if (0 >= timeout)
    {
        GMAC_ERROR("timeout when do mdio read 2");
        return GMAC_ERR_TIMEOUT;
    }

    *pRegVal = GMAC_MII_DATA_GD_MASK & GMAC_READ_REG(pCtrl, GMAC_GMII_DATA_OFFSET);
    return GMAC_SUCCESS;
}

/**
 * @name: GmacPhyWriteReg
 * @msg: write PHY register through MII interface
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 * @param {u16} phyReg
 * @param {u32} regVal
 */
u32 GmacPhyWriteReg(GmacCtrl *pCtrl, u16 phyReg, u32 regVal)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 cmdRegVal = 0, readRegVal;
    GmacConfig *pConfig = &pCtrl->config;
    int timeout = GMAC_RETRY_TIMES;

    cmdRegVal |= GMAC_MII_ADDR_CR(pConfig->clkMDC);
    cmdRegVal |= GMAC_MII_ADDR_PA(pConfig->phyAddr);
    cmdRegVal |= GMAC_MII_ADDR_GR(phyReg);
    cmdRegVal |= GMAC_MII_ADDR_GW;
    cmdRegVal |= GMAC_MII_ADDR_GB;

    GMAC_WRITE_REG(pCtrl, GMAC_GMII_DATA_OFFSET, regVal);
    GMAC_WRITE_REG(pCtrl, GMAC_GMII_ADDR_OFFSET, cmdRegVal);

    /* Check for the Busy flag */
    do
    {
        fsleep_millisec(1);
        readRegVal = GMAC_READ_REG(pCtrl, GMAC_GMII_ADDR_OFFSET);
    } while ((GMAC_MII_ADDR_GB & readRegVal) && (0 <= --timeout));    

    if (0 >= timeout)
    {
        GMAC_ERROR("timeout when do mdio read 2");
        return GMAC_ERR_TIMEOUT;
    }

    return GMAC_SUCCESS;
}

/**
 * @name: GmacPhyGetAddr
 * @msg: get phy id
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 */
u32 GmacPhyGetAddr(GmacCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    GmacConfig *pConfig = &pCtrl->config;
    u32 i;
    u32 phyId1 = 0, phyId2 = 0;
    u32 ret = GMAC_ERR_PHY_NOT_SUPPORT;

    for (i = 0; i < PHY_MAX_NUM; i++)
    {
        ret = GmacPhyReadReg(pCtrl, PHY_MII_PHYSID1_REG, &phyId1);
        ret |= GmacPhyReadReg(pCtrl, PHY_MII_PHYSID2_REG, &phyId2);

        if (GMAC_SUCCESS != ret)
        {
            break;
        }

        if ((GMAC_PHY_ID1 == phyId1) && (GMAC_PHY_ID2 == phyId2))
        {        
            pConfig->phyAddr = i;
            ret = GMAC_SUCCESS;
            GMAC_INFO("phy id: [0x%08x][0x%08x], phy addr: [%d], ret: 0x%x",
                      phyId1, phyId2, i, ret);
            break;            
        }
    }

    return ret;
}

/**
 * @name: GmacPhyInit
 * @msg: init phy and negotiation
 * @return {*}
 * @param {GmacCtrl} *pCtrl
 */
u32 GmacPhyInit(GmacCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 ret = GMAC_SUCCESS;
    u32 regVal;
    u32 timeout = 0;
    GmacConfig *pConfig = &pCtrl->config;

    /*-------------------- PHY initialization and configuration ----------------*/
    /* Power-down PHY */
    ret = GmacPhyWriteReg(pCtrl, PHY_MII_CTRL_REG, PHY_MII_CR_POWER_DOWN);
    fsleep_microsec(PHY_DELAY_US);
    ret |= GmacPhyWriteReg(pCtrl, PHY_MII_CTRL_REG, 0); /* Power-on */
    if (GMAC_SUCCESS != ret)
    {
        GMAC_ERROR("power down phy failed");
        return ret;
    }

    /* Put the PHY in reset mode */
    // ret = GmacPhyWriteReg(pCtrl, PHY_MII_CTRL_REG, PHY_MII_CR_RESET);
    // if (GMAC_SUCCESS != ret)
    // {
    //     GMAC_ERROR("reset phy failed");
    //     return ret;
    // }

    // do
    // {
    //     GmacPhyReadReg(pCtrl, PHY_MII_CTRL_REG, &regVal);
    //     fsleep_microsec(PHY_DELAY_US);
    // } while ((PHY_MII_CR_RESET == (regVal & PHY_MII_CR_RESET)) && (GMAC_RETRY_TIMES > ++timeout));

    // if (GMAC_RETRY_TIMES <= timeout)
    // {
    //     GMAC_ERROR("reset phy timeout");
    //     ret = GMAC_ERR_TIMEOUT;
    //     goto init_done;
    // }
    
    /* Auto negotiation */
    if (GMAC_AUTONEGOTIATION_ENABLE == pConfig->autoNegot)
    {
        do
        {
            GmacPhyReadReg(pCtrl, PHY_MII_STATUS_REG, &regVal);
            fsleep_microsec(PHY_DELAY_US);
        } while ((PHY_MII_SR_LSTATUS != (PHY_MII_SR_LSTATUS & regVal)));

        ret = GmacPhyWriteReg(pCtrl, PHY_MII_CTRL_REG, PHY_MII_CR_AUTO_NEGOT | PHY_MII_CR_RESTART_AUTO_NEGO);
        if (GMAC_SUCCESS != ret)
        {
            GMAC_ERROR("auto negotiation failed");
            goto init_done;
        }

        timeout = 0;
        do
        {
            GmacPhyReadReg(pCtrl, PHY_MII_STATUS_REG, &regVal);

        } while ((PHY_MII_SR_AUTO_NEGOT_COMPLETE != (PHY_MII_SR_AUTO_NEGOT_COMPLETE & regVal)) && 
                 (GMAC_RETRY_TIMES < ++timeout));
        
        if (GMAC_RETRY_TIMES <= timeout)
        {
            ret = GMAC_ERR_TIMEOUT;
            GMAC_ERROR("auto negotiation timeout");
            goto init_done;
        }

        /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
        if (PHY_MII_SR_100HALF != (PHY_MII_SR_100HALF & regVal))
        {
            pConfig->duplexMode = GMAC_MODE_HALFDUPLEX;
        }
        else
        {
            pConfig->duplexMode = GMAC_MODE_FULLDUPLEX;
        }
    }
    else
    {
        regVal = 0;
        if (GMAC_MODE_FULLDUPLEX == pConfig->duplexMode)
        {
            if (GMAC_SPEED_1000 == pConfig->speed)
            {
                regVal = PHY_MII_CR_FULLDUPLEX_1000M;
            }
            else if (GMAC_SPEED_100 == pConfig->speed)
            {
                regVal = PHY_MII_CR_FULLDUPLEX_100M;
            }
            else if (GMAC_SPEED_10 == pConfig->speed)
            {
                regVal = PHY_MII_CR_FULLDUPLEX_10M;
            }
        }
        else
        {
            if (GMAC_SPEED_1000 == pConfig->speed)
            {
                regVal = PHY_MII_CR_HALFDUPLEX_1000M;
            }
            else if (GMAC_SPEED_100 == pConfig->speed)
            {
                regVal = PHY_MII_CR_HALFDUPLEX_100M;
            }
            else if (GMAC_SPEED_10 == pConfig->speed)
            {
                regVal = PHY_MII_CR_HALFDUPLEX_10M;
            }

            /* AutoNegotiation Disable */
            ret = GmacPhyWriteReg(pCtrl, PHY_MII_CTRL_REG, regVal);
            if (GMAC_SUCCESS != ret)
            {
                GMAC_ERROR("disable auto-negotiation failed");
                goto init_done;
            }
        }
    }

    fsleep_microsec(PHY_DELAY_US);

#ifdef CONFIG_F_GMAC_PHY_AR803X
    ret = GmacAr803xDisableHibernate(pCtrl);
    if (GMAC_SUCCESS != ret)
    {
        GMAC_ERROR("hibernate disable failed");
        return ret;
    }
#endif

init_done:
    return ret;    
}